// import com.alibaba.druid.FastsqlColumnAmbiguousException;
// import com.alibaba.druid.FastsqlException;
// import com.alibaba.druid.sql.SQLUtils;
// import com.alibaba.druid.sql.ast.*;
// import com.alibaba.druid.sql.ast.expr.SQLBinaryOpExpr;
// import com.alibaba.druid.sql.ast.expr.SQLIdentifierExpr;
// import com.alibaba.druid.sql.ast.expr.SQLPropertyExpr;
// import com.alibaba.druid.sql.repository.SchemaResolveVisitor;
// import com.alibaba.druid.sql.visitor.SQLASTVisitor;
// import com.alibaba.druid.util.FnvHash;

// import java.io.IOException;
// import java.util.ArrayList;
// import java.util.std::list;

#pragma once

#include <string>
#include <list>

#include "SQLTableSource.h"
#include "../SQLExpr.h"
#include "../SQLExprImpl.h"
#include "../Object.h"
#include "../SQLName.h"
#include "../SQLReplaceable.h"

// #include "../../../utils/enumdecl.h"
// #include "../../visitor/SQLASTVisitor.h"

#include "SQLAssignItem.h"
#include "SQLTableSourceImpl.h"
#include "SQLSelectOrderByItem.h"

class SQLASTVisitor;

class SQLJoinTableSource_JoinType
{
public:
  static SQLJoinTableSource_JoinType COMMA;
  static SQLJoinTableSource_JoinType JOIN;
  static SQLJoinTableSource_JoinType INNER_JOIN;
  static SQLJoinTableSource_JoinType CROSS_JOIN;
  static SQLJoinTableSource_JoinType NATURAL_JOIN;
  static SQLJoinTableSource_JoinType NATURAL_CROSS_JOIN;
  static SQLJoinTableSource_JoinType NATURAL_LEFT_JOIN;
  static SQLJoinTableSource_JoinType NATURAL_RIGHT_JOIN;
  static SQLJoinTableSource_JoinType NATURAL_INNER_JOIN;
  static SQLJoinTableSource_JoinType LEFT_OUTER_JOIN;
  static SQLJoinTableSource_JoinType LEFT_SEMI_JOIN;
  static SQLJoinTableSource_JoinType LEFT_ANTI_JOIN;
  static SQLJoinTableSource_JoinType RIGHT_OUTER_JOIN;
  static SQLJoinTableSource_JoinType FULL_OUTER_JOIN;
  static SQLJoinTableSource_JoinType STRAIGHT_JOIN;
  static SQLJoinTableSource_JoinType OUTER_APPLY;
  static SQLJoinTableSource_JoinType CROSS_APPLY;

  uint64_t mark;
  string_ptr name;
  string_ptr nameLCase;

  SQLJoinTableSource_JoinType();
  SQLJoinTableSource_JoinType(SQLJoinTableSource_JoinType *joinType);
  SQLJoinTableSource_JoinType(int mark_, string_ptr name_);

  BOOL_ptr operator==(const SQLJoinTableSource_JoinType &ps);
  BOOL_ptr operator!=(const SQLJoinTableSource_JoinType &ps);
  static string_ptr toString(SQLJoinTableSource_JoinType_ptr joinType) { return joinType->name; }
};

class SQLJoinTableSource_UDJ : virtual public SQLObjectImpl
{
public:
  SQLExpr_ptr function;
  SQLExpr_list_ptr arguments;
  string_ptr alias;
  SQLName_list_ptr columns;
  SQLSelectOrderByItem_list_ptr sortBy;
  SQLAssignItem_list_ptr properties;

  SQLJoinTableSource_UDJ();
  SHAREDOBJECT(SQLJoinTableSource_UDJ);

  void accept0(SQLASTVisitor_ptr v);

  SQLSelectOrderByItem_list_ptr getSortBy();

  SQLJoinTableSource_UDJ(SQLExpr_ptr function);

  // SQLJoinTableSource_UDJ *clone();
  virtual SQLObject_ptr clone();
  SQLExpr_ptr getFunction();

  void setFunction(SQLExpr_ptr function);

  SQLExpr_list_ptr getArguments();

  SQLName_list_ptr getColumns();

  string_ptr getAlias();

  void setAlias(string_ptr alias);

  SQLAssignItem_list_ptr getProperties();
};

class SQLJoinTableSource : virtual public SQLTableSourceImpl, virtual public SQLReplaceable
{
public:
  static string_ptr class_;
  SQLTableSource_ptr left;
  SQLJoinTableSource_JoinType_ptr joinType;
  SQLTableSource_ptr right;
  SQLExpr_ptr condition;
  SQLExpr_list_ptr using_list;
  BOOL_ptr natural;
  SQLJoinTableSource_UDJ_ptr udj; // for maxcompute
  BOOL_ptr asof;                      // for clickhouse
  BOOL_ptr global;                    // for clickhouse

  SQLJoinTableSource(string_ptr alias);

  SQLJoinTableSource();

  SQLJoinTableSource(SQLTableSource_ptr left, SQLJoinTableSource_JoinType_ptr joinType, SQLTableSource_ptr right, SQLExpr_ptr condition);
  SHAREDOBJECT(SQLJoinTableSource);

  void accept0(SQLASTVisitor_ptr visitor);

  SQLJoinTableSource_UDJ_ptr getUdj();

  void setUdj(SQLJoinTableSource_UDJ_ptr x);

  BOOL_ptr isAsof();

  void setAsof(BOOL_ptr asof);

  SQLJoinTableSource_JoinType_ptr getJoinType();

  void setJoinType(SQLJoinTableSource_JoinType_ptr joinType);

  void setImplicitJoinToCross();

  SQLTableSource_ptr getLeft();

  void setLeft(SQLTableSource_ptr left);

  void setLeft(string_ptr tableName, string_ptr alias);

  void setRight(string_ptr tableName, string_ptr alias);

  SQLTableSource_ptr getRight();

  void setRight(SQLTableSource_ptr right);

  SQLExpr_ptr getCondition();

  void setCondition(SQLExpr_ptr condition);

  void addCondition(SQLExpr_ptr condition);

  void addConditionnIfAbsent(SQLExpr_ptr condition);

  BOOL_ptr containsCondition(SQLExpr_ptr condition);

  SQLExpr_list_ptr getUsing();

  BOOL_ptr isNatural();

  void setNatural(BOOL_ptr natural);

  // void output(Appendable* buf) ;

  BOOL_ptr replace(SQLExpr_ptr expr, SQLExpr_ptr target);

  BOOL_ptr replace(SQLTableSource_ptr cmp, SQLTableSource_ptr target);

  void cloneTo(SQLJoinTableSource_ptr x);

  virtual SQLObject_ptr clone();
  void reverse();

  /**
   * a inner_join (b inner_join c) -&lt; a inner_join b innre_join c
   */
  void rearrangement();

  BOOL_ptr contains(SQLTableSource_ptr tableSource, SQLExpr_ptr condition);

  BOOL_ptr contains(SQLTableSource_ptr tableSource, SQLExpr_ptr condition, SQLJoinTableSource_JoinType_ptr joinType);

  SQLJoinTableSource *findJoin(SQLTableSource_ptr tableSource, SQLJoinTableSource_JoinType_ptr joinType);

  BOOL_ptr containsAlias(string_ptr alias);

  SQLColumnDefinition_ptr findColumn(string_ptr  columnName) ;
  SQLColumnDefinition_ptr findColumn(long columnNameHash) ;

  SQLObject_ptr resolveColum(long columnNameHash);

  SQLTableSource_ptr findTableSourceWithColumn(string_ptr columnName);

  SQLJoinTableSource *findTableSourceWithColumn(SQLName_ptr a, SQLName_ptr b);

  SQLTableSource_ptr findTableSourceWithColumn(long columnNameHash, string_ptr name, int option);

  BOOL_ptr match(string_ptr alias_a, string_ptr alias_b);

  BOOL_ptr conditionContainsTable(string_ptr alias);

  SQLJoinTableSource *join(SQLTableSource_ptr right, SQLJoinTableSource_JoinType_ptr joinType, SQLExpr_ptr condition);

  SQLTableSource_ptr findTableSource(long alias_hash);

  SQLTableSource_ptr other(SQLTableSource_ptr x);

  BOOL_ptr isGlobal();

  void setGlobal(BOOL_ptr global);

  int hashCode();

  bool equals(Object_ptr o);

  void splitTo(SQLTableSource_list_ptr  outTableSources, SQLJoinTableSource_JoinType_ptr joinType);
};
